/*
 * ===================================================================================
 *
 *       Filename:  PokerHand.cpp
 *
 *    Description:  the implementation of PokerHand.h
 *
 *        Version:  1.0
 *        Created:  11/08/11 02:38:09
 *       Revision:  none
 *       Compiler:  g++
 *
 *         Author:  LittleQ Su (), 98703060@nccu.edu.tw
 *        Company:  National Chengchi University, The Department of Computer Science
 *
 * ===================================================================================
 */
#include "headers.h"

PokerHand::PokerHand() {
    this -> numInHand = 0;
    _rankCount_local();
}

PokerHand::PokerHand(Deck & deck) {
    this -> numInHand = 0;
    this -> dealAll(deck);
}

PokerHand::PokerHand(const Card &c1, const Card &c2, const Card &c3, const Card &c4, const Card &c5) {
    this -> hand[0] = c1;
    this -> hand[1] = c2;
    this -> hand[2] = c3;
    this -> hand[3] = c4;
    this -> hand[4] = c5;
    this -> numInHand = 5;
    _rankCount_local();

}

void PokerHand::dealAll(Deck & deck) {
    this -> numInHand = 0;
    for ( int i = 0 ; i < this -> COUNT ; ++ i ){
        this -> dealOne(deck);
    }
}

void PokerHand::dealOne(Deck & deck) {
    if (this -> numInHand >= this -> COUNT ) return ;

    this -> hand[this->numInHand] = deck.dealCard();
    this -> numInHand++;
    _rankCount_local();

}

Card PokerHand::getCard(int index) const{
    return this -> hand[index];
}

Card::Suit PokerHand::getCardSuit(int index) const{
    return (this -> hand[index]).getSuit();
}

int PokerHand::getCardRank(int index) const{
    return (this -> hand[index]).getRank();
}

void PokerHand::print() const{
    for (int i = 0 ; i < this->numInHand ; ++i ){
        cout << "Total: " << this -> numInHand << " card(s)." << endl;
        cout << (this -> hand[i]).toString() << endl;
    }
}

void PokerHand::rankCount(int * counts) const {
    memset( counts , 0 , sizeof(counts) );
    
    for ( int i = 0 ; i < this->numInHand ; i++) {
        counts[ ((this->hand[i]).getRank() - 1)]+=1;
    }

}

bool PokerHand::isRoyalFlush() const {
    return (this->isStraight() == 9 && this->isFlush());
}
bool PokerHand::isStraightFlush() const{
    return (this->isStraight() != 0 && this->isFlush());
}
int  PokerHand::isFourOfKind() const {
    return ( _sameNumTimes(4) == 1 ) ? leaderRank : 0;
}
bool PokerHand::isFullHouse() const {
    return ( _sameNumTimes(3) == 1 && _sameNumTimes(2) == 1 );
}
bool PokerHand::isFlush() const{
    Card::Suit temp = this -> getCardSuit(0);

    for ( int i = 1; i < HAND_MAX ; ++i ) {
        if ( this -> getCardSuit(i) != temp ) return false;
    }

    return true;
    
}
int  PokerHand::isStraight() const{
    int start = 0;
    for ( int i = 0 ; i < 10 ; ++ i ) {
        if ( ranks[i]  > 0 ) { start = i ; break;}
    }

    for ( int i = start ; i < start+5 ; ++ i) {
        if ( ranks[i%13] != 1 ) { return 0; } 
    }

    return start+1;
}
int  PokerHand::isThreeOfKind() const{
    return ( _sameNumTimes(3) == 1 && _sameNumTimes(2) == 0 ) ? leaderRank : 0;
}
bool PokerHand::isTwoPair() const{
    return ( _sameNumTimes(2) == 2 );
}
int  PokerHand::isOnePair() const{
    return ( this->_sameNumTimes(1) == 3 && _sameNumTimes(2) == 1 ) ? leaderRank : 0;
}


/*  Private  */
bool PokerHand::_handIsFull() const {
    if (this -> numInHand == 5) return true;
    else {
        cout << "Hand card is not enough." << endl;
        return false;
    }

}

int PokerHand::_sameCardMaxCount(){
    int max = 1;


    for ( int i = 0 ; i < RANK_MAX ; ++i ) {
        //max = (this->ranks[i] > max) ? this->ranks[i] : max ;
        if ( ranks[i] > max ) {
            max = ranks[i];
        }
    }

    return max;
}

int PokerHand::_sameNumTimes(int num) const{
    int count = 0;

    for (int i = 0 ; i < RANK_MAX ; ++i) {
        if ( this->ranks[i] == 2 ) count ++ ;
    }

    return count;

}

void PokerHand::_rankCount_local() {
    int max_rank = 0;
    memset( ranks , 0 , sizeof(ranks) );
    
    for ( int i = 0 ; i < this->numInHand ; i++) {
        ranks[ ((this->hand[i]).getRank() - 1)]+=1;
    }
    
    for ( int i = 0 ; i < RANK_MAX ; i++ ) {
        if ( ranks[i] > ranks[max_rank] ) max_rank = i; 
    }

    this ->leaderRank = max_rank;

}
